// 基础的
// 空字符模式
// 单字符模式

import { FARule } from "./dfa"
import { NFADesign, NFARuleBook } from "./nfa"

// 连接模式  
// 选择模式  或关系
// 重复模式  零次或者多次

class Pattern {
    precedence: number = 1
    toString() {
        return ''
    }

    bracket(outer_precedence: number) {
        if (this.precedence < outer_precedence) {
            return '(' + this.toString() + ')'
        } else {
            return this.toString()
        }
    }
    toNFA(){
        let start_state = Symbol();
        let accept_states = [start_state]
        let rulebook = new NFARuleBook([])
        return new NFADesign(
            start_state,
            accept_states,
            rulebook
        )
    }
    matches(input:string){
        return this.toNFA().accepts(input)
    }
}

// '' 空
class Empty extends Pattern {
    precedence: number = 3
    toString() {
        return ''
    }
    toNFA(){
        let start_state = Symbol();
        let accept_states = [start_state]
        let rulebook = new NFARuleBook([])
        return new NFADesign(
            start_state,
            accept_states,
            rulebook
        )
    }
}
// a 单字母
class Literal extends Pattern {
    precedence: number = 3

    constructor(
        public character: string = ''
    ) {
        super()
    }

    toString() {
        return this.character
    }

    toNFA(){
        let start_state = Symbol(1);
        let accept_state = Symbol(2)
        let rule = new FARule(start_state,this.character,accept_state)
        let rulebook = new NFARuleBook([rule])
        return new NFADesign(
            start_state,
            [accept_state],
            rulebook
        )
    }
    
}

// ab 连接
class Concatenate extends Pattern {
    precedence: number = 1

    constructor(
        public first: Pattern,
        public second: Pattern
    ) {
        super()
    }

    toString(): string {
        return [this.first, this.second].map(pattern => {
            return pattern.bracket(this.precedence)
        }).join('')
    }
    toNFA(): NFADesign {
        let first_nfa = this.first.toNFA()
        let second_nfa = this.second.toNFA()

        let start_state = first_nfa.start_state;
        let accept_states = second_nfa.accept_states;

        let rules = first_nfa.rulebook.rules.concat(
            second_nfa.rulebook.rules
        )
         
        first_nfa.accept_states.forEach(fstate => {
            rules.push(new FARule(fstate,'',second_nfa.start_state))
        })

        let rulebook = new NFARuleBook(rules)
        return new NFADesign(start_state,accept_states,rulebook)
    }
}

// a|b 或连接
class Choose extends Pattern {
    precedence: number = 0
    constructor(
        public first: Pattern,
        public second: Pattern
    ) {
        super()
    }
    toString(): string {
        return [this.first, this.second].map(pattern => {
            return pattern.bracket(this.precedence)
        }).join("|")
    }
    toNFA(): NFADesign {
        let first_nfa = this.first.toNFA()
        let second_nfa = this.second.toNFA()

        let start_state = Symbol(1)
        let accept_states = first_nfa.accept_states.concat(
            second_nfa.accept_states
        )
        let rules = first_nfa.rulebook.rules.concat(
            second_nfa.rulebook.rules
        )
        rules.push(new FARule(start_state,'',first_nfa.start_state))
        rules.push(new FARule(start_state,'',second_nfa.start_state))
            
        let rulebook = new NFARuleBook(rules)
        return  new NFADesign(start_state,accept_states,rulebook)
    }
}

// a* 重复
class Repeat extends Pattern {
    precedence: number = 2;
    constructor(
        public pattern: Pattern
    ) {
        super()
    }
    toString(): string {
        return this.pattern.bracket(this.precedence) + '*';
    }
    toNFA(): NFADesign {
        let nfa = this.pattern.toNFA()
        let start_state = Symbol(1)
        let accept_states = nfa.accept_states
        accept_states.push(start_state)

        let rules = nfa.rulebook.rules;
        nfa.accept_states.forEach(accept_state => {
            rules.push(new FARule(accept_state,'',nfa.start_state))
        })
        rules.push(new FARule(start_state,'',nfa.start_state))
        let rulebook = new NFARuleBook(rules)
        return new NFADesign(start_state,accept_states,rulebook)
    }
}

// let pattern = new Repeat(
//     new Choose(
//         new Concatenate(new Literal('a'), new Literal('b')),
//         new Literal('a')
//     )
// )
// console.log(pattern.toString())
// let nfa = new Empty().toNFA()
// console.log(nfa.accepts('')) 
// console.log(nfa.accepts('a')) 

// let nfa2 = new Literal('a').toNFA()
// console.log(nfa2.accepts('')) 
// console.log(nfa2.accepts('a')) 
// console.log(nfa2.accepts('b')) 


// let pattern = new Concatenate(
//     new Literal('a'),new Literal('b')
// )
// console.log(pattern.toString())
// // console.log(pattern.toNFA())
// console.log(pattern.matches('a'))
// console.log(pattern.matches('b'))
// console.log(pattern.matches('ab'))
// console.log(pattern.matches('abc'))

// let p = new Concatenate(
//     new Literal('a'),
//     new Concatenate(new Literal('b'),new Literal('c'))
// )

// console.log(p.toString())
// console.log(p.matches('a'))
// console.log(p.matches('ab'))
// console.log(p.matches('abc'))

// let p = new Choose(
//     new Literal('a'),
//     new Literal('b')
// )
// console.log(p.toString())

// console.log(p.matches('a'))
// console.log(p.matches('b'))
// console.log(p.matches('ab'))
// console.log(p.matches('c'))

// let p = new Repeat(new Literal('a'))
// console.log(p.toString())
// console.log(p.matches(''))
// console.log(p.matches('a'))
// console.log(p.matches('aaa'))
// console.log(p.matches('aba'))

let p = new Repeat(
    new Concatenate(
        new Literal('a'),
        new Choose(new Empty(),new Literal('b'))
    )
)
console.log(p.toString())
